package search.service.graph;

import com.thinkaurelius.titan.core.TitanGraph;
import common.model.graph.CommonSchema;
import common.model.graph.ConceptSchema;
import common.model.graph.InstanceSchema;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
import org.apache.tinkerpop.gremlin.structure.Direction;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.apache.tinkerpop.gremlin.structure.VertexProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import search.config.AnalyzerNames;
import common.dao.elastic.ElasticSearchDao;
import common.dao.gremlin.TitanGraphDao;
import search.service.analysis.AnalysisService;

import javax.inject.Inject;
import java.util.*;

/**
 * Created by Common on 8/31/16.
 */
@Component
public class EntitySearchService {

    final Logger logger = LoggerFactory.getLogger(EntitySearchService.class);


    private ElasticSearchDao esDao;
    //private TitanGraphDao titandb;
    //private TitanGraph graph;
    private GraphTraversalSource g;

    @Inject
    public EntitySearchService(
            ElasticSearchDao esDao
            //,TitanGraphDao titandb
    ){
        this.esDao = esDao;
        //this.titandb = titandb;
        //this.graph = this.titandb.getTitanGraph();
        //this.g = this.graph.traversal();
    }


    public String entitySearchService(String query){
        /*
        目标：
        1) parse query to multiple token;
        2) search every token;
        3) return  most import token;
         */

        /*
        now:
            return all tokens;
         */
        String analyzer_str = AnalyzerNames.ik_smart;
        List<String> tokenList = AnalysisService.getTokenTermList(this.esDao, query, analyzer_str);
        logger.debug("[token list] length = " + tokenList.size() );

        Set<String> tokens = new HashSet<>(tokenList);

        Map<String, Map<String, List<String>> > result = new HashMap<>();

        for(String token: tokens){
            Map<String, Map<String, List<String>> > entites = entitySearchByToken(token, null );
            result.putAll( entites );
        }

        String json = GraphServiceHelper.outputMapMapToJson(result);
        return json;
    }


    public String entitySearchJson(String token){
        Map<String, Map<String, List<String>> > entites = entitySearchByToken(token, null );
        String json = GraphServiceHelper.outputMapMapToJson(entites);
        return json;
    }


    public String instanceSearchJson(String token){
        Map<String, Map<String, List<String>> > instances = entitySearchByToken(token, InstanceSchema.Properties.has_label_value);
        String json = GraphServiceHelper.outputMapMapToJson(instances);
        return json;
    }

    public String conceptSearchJson(String token){
        Map<String, Map<String, List<String>> > concepts = entitySearchByToken(token, ConceptSchema.Properties.has_label_value );
        String json = GraphServiceHelper.outputMapMapToJson(concepts);
        return json;
    }


    public Map< String, List<String> > parseVertex(Vertex vertex){
        logger.info("[parseVertex] parseVertex ...");
        Map<String,List<String> > kvs = new HashMap< >();
        String vid = vertex.value( CommonSchema.Properties.has_id ).toString();
        String rid = vertex.id().toString();
        logger.debug("[Vertex] vertex virtual id = " + vid + ", real id = " + rid);

        // property
        Iterator<VertexProperty<Object>> ps = vertex.properties();
        while (ps.hasNext()) {
            VertexProperty<Object> p = ps.next();
            GraphServiceHelper.insertValueByKey(kvs, p.key(), p.value().toString() );
        }

        // edge by vertex
        Iterator<Edge> es = vertex.edges( Direction.OUT );
        while( es.hasNext() ){
            Edge e = es.next();
            String eid = e.id().toString();
            //logger.debug("[edge by vertex] edge id = " + eid);
            String key = e.label();
            Vertex inv = e.inVertex();
            String value = GraphServiceHelper.getNameZh(inv);
            GraphServiceHelper.insertValueByKey(kvs, key, value );
        }

        return kvs;
    }

    public Map<String, Map<String, List<String>> >  entitySearchByToken(String token, String typeLabel){
        logger.info("[entitySearchByToken]  token = " + token);
        GraphTraversal vertexes = this.g.V()
                .has(CommonSchema.Properties.has_name_zh , token );
        // parse typeLabel
        /*
        none->empty
        concept
        instance
         */
        if(typeLabel != null && !typeLabel.equals("")){
            if(typeLabel.equals(ConceptSchema.Properties.has_label_value))
                vertexes.hasLabel(ConceptSchema.Properties.has_label_value);
            else if(typeLabel.equals(InstanceSchema.Properties.has_label_value) )
                vertexes.hasLabel(InstanceSchema.Properties.has_label_value);
        }
        logger.info("[entitySearchByToken] gened travesal lang, begin to generate answer!");
        Map<String, Map<String, List<String>> > map = new HashMap<>();
        while(vertexes.hasNext()){
            Vertex vertex = (Vertex)vertexes.next();
            Map< String, List<String> > vertex_map = parseVertex(vertex);
            String vertex_name = GraphServiceHelper.getNameZh(vertex);
            String vertex_id = vertex.value( CommonSchema.Properties.has_id ).toString();
            // src + target + id
            String key = token + "_" + vertex_name + "_" + vertex_id;
            map.put(key, vertex_map);
        }
        logger.info("[entitySearchByToken] traversal token " + token + " finished!");

        return map;
    }


    public Map< String, List<String> > entitySearchByTokenTest(String token, String typeLabel){
        //List< Pair<String,String> > pairs = null;
        Map<String,List<String> > kvs = null;
        GraphTraversal vertexes = this.g.V()
                .has(CommonSchema.Properties.has_name_zh , token );
        if(typeLabel != null && !typeLabel.equals("")){
            if(typeLabel.equals(ConceptSchema.Properties.has_label_value))
                vertexes.hasLabel(ConceptSchema.Properties.has_label_value);
            else if(typeLabel.equals(InstanceSchema.Properties.has_label_value) )
                vertexes.hasLabel(InstanceSchema.Properties.has_label_value);
        }

        if( !vertexes.hasNext() )
            return kvs;

        kvs = new HashMap< >();
        //pairs = new ArrayList< Pair<String, String> >();

        // only pick one now
        Vertex vertex = (Vertex)vertexes.next();
        // parse vertex
        String vid = vertex.value( CommonSchema.Properties.has_id ).toString();
        String rid = vertex.id().toString();
        logger.debug("[Vertex] vertex virtual id = " + vid + ", real id = " + rid);
        // property
        Iterator<VertexProperty<Object>> ps = vertex.properties();
        while (ps.hasNext()) {
            VertexProperty<Object> p = ps.next();
            //kvs.put(p.key(), p.value().toString() );
            //pairs.add( new Pair(p.key(), p.value().toString() ) );
            GraphServiceHelper.insertValueByKey(kvs, p.key(), p.value().toString() );
        }


        //1） edge by vertex
        // not use
        Iterator<Edge> es = vertex.edges( Direction.OUT );
        while( es.hasNext() ){
            Edge e = es.next();
            String eid = e.id().toString();
            //logger.debug("[edge by vertex] edge id = " + eid);
            String key = e.label();
            Vertex inv = e.inVertex();
            String value = GraphServiceHelper.getNameZh(inv);
            //pairs.add( new Pair<>(key,value) );
            GraphServiceHelper.insertValueByKey(kvs, key, value );
        }


        //2) edge by edge
        // not use
        //edges by travelsal graph
        /*
        GraphTraversal edges = this.g.V()
                .has(CommonSchema.Properties.has_name_zh, token)
                .hasLabel(ConceptSchema.Properties.has_label_value)
                .outE();
        while ( edges.hasNext() ){
            Edge e = (Edge)edges.next();
            String eid = e.id().toString();
            Vertex outv = e.outVertex();
            String vvid = outv.value( CommonSchema.Properties.has_id ).toString();
            String vrid = outv.id().toString();
            //if( !vrid.equals(rid) || !vvid.equals(vid) ) continue;
            String key = e.label();
            Vertex inv = e.inVertex();
            String value = GraphServiceHelper.getNameZh(inv);
            logger.debug("[edge by edge] edge id = " + eid);
            //kvs.put( key, value );
        }
        */


        // 3) edge by path
        // using
        //paths by travelsal graph
        /*
        http://stackoverflow.com/questions/33676566/gremlin-get-all-incoming-and-outgoing-vertex-including-their-edges-and-directio
        http://tinkerpop.apache.org/javadocs/3.1.3/full/org/apache/tinkerpop/gremlin/process/traversal/Path.html
         */
        /*
        GraphTraversal paths = this.g.V()
                .has(CommonSchema.Properties.has_name_zh , token )
                .hasLabel(ConceptSchema.Properties.has_label_value)
                .outE()
                .inV()
                .path();
                //.by( __.valueMap(true) );
        int minPathSize = 3; //[out , e, in]
        while( paths.hasNext() ){
            Path path = (Path)paths.next();
            int size = path.size();
            if(size < minPathSize ) {
                logger.error("invalid path, length is smaller than " + minPathSize);
                continue;
            }
            Vertex srcv = path.get(size-3);
            String srcid = srcv.id().toString();
            if(!srcid.equals(rid)) {
                continue;
            }
            String srcname = GraphServiceHelper.getNameZh(srcv);
            Vertex endv = path.get(size-1);
            String endname = GraphServiceHelper.getNameZh(endv);
            Edge edge = path.get(size-2);
            String eid = edge.id().toString();
            String edgename = edge.label();
            logger.debug("[edge by path] edge id = " + eid + ", src = " + srcname + ", edge = " + edgename + ", end = " + endname );
            //kvs.put(edgename, endname);
        }
        */


        return kvs;
    }




    /*************************************
     *
     * @param args
     */
    public static void main(String[] args){
        EntitySearchService service = new EntitySearchService( new ElasticSearchDao("debug"));//, new TitanGraphDao("debug") );

        //List<Pair<String,String> >
        /*
        Map<String, List<String> > concept = service.conceptSearchByToken("食用菌");
        Iterator it = concept.entrySet().iterator();
        while( it.hasNext() ){
            Map.Entry pair = (Map.Entry)it.next();
            String key = pair.getKey().toString();
            List<String> valueset = (List<String>)pair.getValue();
            for(String v : valueset)
                System.out.println(key + " = " + v);
            it.remove();
        }
        */

        /*
        String json1 = service.conceptSearchJson("食用菌");
        System.out.println(json1);

        String json2 = service.instanceSearchJson("金针菇");
        System.out.println(json2);

        String json3 = service.instanceSearchJson("银耳");
        System.out.println(json3);
        */

        String json = service.entitySearchService("金针菇是一种食用菌");
        //String json = service.entitySearchService("AS2796");
        //String json = service.entitySearchJson("AS2796");
        System.out.println(json);

        System.out.println("execution finished");
    }

}
